﻿using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using RabbitMQ.Client;
using SBlogCore.Common.DispatchProxyIn;
using SBlogCore.EventBus.CrossProcessEventBus.Attributes;
using SBlogCore.EventBus.CrossProcessEventBus.Model;
using SBlogCore.EventBus.CrossProcessEventBus.MQClient;
using SBlogCore.EventBus.CrossProcessEventBus.Proxy;

//using SBlogCore.EventBus.RabbitMQExtend;

namespace SBlogCore.EventBus.CrossProcessEventBus
{
    public static class CrossEventBusSetUp
    {
        /// <summary>
        /// 注册跨进程EventBus
        /// </summary>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <param name="optionAction"></param> 
        public static void AddCrossEventBus(this IServiceCollection services, IEnumerable<Assembly> assemblies, Action<EventBusOption> optionAction)
        {
            //注册代理
            services.AddDispatchProxyService<IEventBusDispatchProxy, EventBusDispatchProxy>();
            var eventMembers = RegisterSubscribe(services, assemblies);
            RegisterPublish(services, optionAction, eventMembers);
        }
        /// <summary>
        /// 注册订阅
        /// </summary>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        private static Dictionary<string, HashSet<EventMember>> RegisterSubscribe(IServiceCollection services, IEnumerable<Assembly> assemblies)
        {
            //注册订阅方法
            var eventMembers = new Dictionary<string, HashSet<EventMember>>();
            //通过 CrossEventHandlerAttribute 特性获取所有需要注册的类
            //因为发送与监听都用到了CrossEventHandlerAttribute 所以要先过滤掉发送的接口类
            var types = assemblies.SelectMany(w =>
                w.GetTypes().Where(t =>!t.IsInterface&& t.GetMethods().Any(m => m.IsDefined(typeof(CrossEventHandlerAttribute)))));
            foreach (var type in types)
            {
                //注入
                services.AddScoped(type);
                //获取所有需要注册的方法
                var methods = type.GetMethods().Where(m => m.IsDefined(typeof(CrossEventHandlerAttribute)));
                foreach (var method in methods)
                {
                    var member = new EventMember(method);
                    if (!eventMembers.ContainsKey(member.EventName))
                    {
                        eventMembers.Add(member.EventName, new HashSet<EventMember>());
                    }

                    eventMembers[member.EventName].Add(member);
                }
            }
            return eventMembers;
        }
        /// <summary>
        /// 注册发布
        /// </summary>
        /// <param name="services"></param>
        /// <param name="optionAction"></param>
        /// <param name="eventMembers"></param>
        /// <exception cref="Exception"></exception>
        private static void RegisterPublish(IServiceCollection services, Action<EventBusOption> optionAction, Dictionary<string, HashSet<EventMember>> eventMembers)
        {
            //注册EventBus发布接口
            services.AddSingleton<IEventBus>(ps =>
            {
                var service = ps.GetRequiredService<IServiceScopeFactory>();

                EventBusOption option = new EventBusOption();
                optionAction.Invoke(option);
                if (string.IsNullOrEmpty(option.Host))
                    throw new Exception("请先为EventBus配置服务器地址！");
                if (string.IsNullOrEmpty(option.QueueName))
                    throw new Exception("请先为EventBus配置队列名称！");
                var factory = new ConnectionFactory
                {
                    HostName = option.Host, //服务器地址
                    Port = option.Port,
                    DispatchConsumersAsync = true //启用异步
                };
                RabbitMqConnection connection = new RabbitMqConnection(factory);
                var eventBus = new EventBusHandler(service, option.QueueName, connection);
                eventBus.Subscribe(eventMembers);

                return eventBus;
            });
        }

    }
}
